/* Copyright 2009
 *
 * This program and the accompanying materials
 * are made available under the terms of the
 * Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at
 *
 * 		http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * Contributors:
 * 	   IBM Corporation - initial API and implementation for JDT/DLTK
 *     Sean W. Quinn - initial adoption for use with PHP from various sources.
 */
package org.eclipse.php.internal.core.ast.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.eclipse.dltk.compiler.CharOperation;

/**
 * <p>
 * This is a temporary class, meant to add functionality to the {@link org.eclipse.php.internal.core.ast.util.Util}
 * class. Ideally this would be merged with that class and references updated.
 * </p>
 * 
 * @author Sean W. Quinn (swquinn@gmail.com)
 */
public class UtilExt {

	private static final int DEFAULT_READING_SIZE = 8192;
	private static final int DEFAULT_WRITING_SIZE = 1024;
	public static final String EMPTY_STRING = new String(CharOperation.NO_CHAR);
	public static final int[] EMPTY_INT_ARRAY= new int[0];

	protected UtilExt() {
	// cannot be instantiated
	}

	/**
	 * Returns the contents of the given file as a byte array.
	 * 
	 * @throws IOException if a problem occured reading the file.
	 */
	public static byte[] getFileByteContent(File file) throws IOException {
		InputStream stream = null;
		try {
			stream = new BufferedInputStream(new FileInputStream(file));
			return getInputStreamAsByteArray(stream, (int) file.length());
		}
		finally {
			if (stream != null) {
				try {
					stream.close();
				}
				catch (IOException e) {
					// ignore
				}
			}
		}
	}

	/**
	 * Returns the given input stream's contents as a byte array. If a length is specified (ie. if length != -1), only
	 * length bytes are returned. Otherwise all bytes in the stream are returned. Note this doesn't close the stream.
	 * 
	 * @throws IOException if a problem occured reading the stream.
	 */
	public static byte[] getInputStreamAsByteArray(InputStream stream, int length) throws IOException {
		byte[] contents;
		if (length == -1) {
			contents = new byte[0];
			int contentsLength = 0;
			int amountRead = -1;
			do {
				int amountRequested = Math.max(stream.available(), DEFAULT_READING_SIZE); // read at least 8K

				// resize contents if needed
				if (contentsLength + amountRequested > contents.length) {
					System.arraycopy(contents, 0, contents = new byte[contentsLength + amountRequested], 0,
							contentsLength);
				}

				// read as many bytes as possible
				amountRead = stream.read(contents, contentsLength, amountRequested);

				if (amountRead > 0) {
					// remember length of contents
					contentsLength += amountRead;
				}
			} while (amountRead != -1);

			// resize contents if necessary
			if (contentsLength < contents.length) {
				System.arraycopy(contents, 0, contents = new byte[contentsLength], 0, contentsLength);
			}
		}
		else {
			contents = new byte[length];
			int len = 0;
			int readSize = 0;
			while ((readSize != -1) && (len != length)) {
				// See PR 1FMS89U
				// We record first the read size. In this case len is the actual
				// read size.
				len += readSize;
				readSize = stream.read(contents, len, length - len);
			}
		}

		return contents;
	}

	/**
	 * Returns the contents of the given zip entry as a byte array.
	 * 
	 * @throws IOException if a problem occured reading the zip entry.
	 */
	public static byte[] getZipEntryByteContent(ZipEntry ze, ZipFile zip) throws IOException {

		InputStream stream = null;
		try {
			InputStream inputStream = zip.getInputStream(ze);
			if (inputStream == null)
				throw new IOException("Invalid zip entry name : " + ze.getName()); //$NON-NLS-1$
			stream = new BufferedInputStream(inputStream);
			return getInputStreamAsByteArray(stream, (int) ze.getSize());
		}
		finally {
			if (stream != null) {
				try {
					stream.close();
				}
				catch (IOException e) {
					// ignore
				}
			}
		}
	}

	/*
	 * Returns the index of the Java like extension of the given file name or -1 if it doesn't end with a known Java
	 * like extension. Note this is the index of the '.' even if it is not considered part of the extension.
	 */
	public static int indexOfPhpLikeExtension(String fileName) {
		int fileNameLength = fileName.length();
		char[][] javaLikeExtensions = Util.getPhpLikeExtensions();
		extensions: for (int i = 0, length = javaLikeExtensions.length; i < length; i++) {
			char[] extension = javaLikeExtensions[i];
			int extensionLength = extension.length;
			int extensionStart = fileNameLength - extensionLength;
			int dotIndex = extensionStart - 1;
			if (dotIndex < 0)
				continue;
			if (fileName.charAt(dotIndex) != '.')
				continue;
			for (int j = 0; j < extensionLength; j++) {
				if (fileName.charAt(extensionStart + j) != extension[j])
					continue extensions;
			}
			return dotIndex;
		}
		return -1;
	}

	/**
	 * Returns true if the given name ends with one of the known java like extension. (implementation is not creating
	 * extra strings)
	 */
	public final static boolean isPhpLikeFileName(String name) {
		if (name == null)
			return false;
		return indexOfPhpLikeExtension(name) != -1;
	}

	/**
	 * Returns true if the given name ends with one of the known java like extension. (implementation is not creating
	 * extra strings)
	 */
	public final static boolean isPhpLikeFileName(char[] fileName) {
		if (fileName == null)
			return false;
		int fileNameLength = fileName.length;
		char[][] phpLikeExtensions = Util.getPhpLikeExtensions();
		extensions: for (int i = 0, length = phpLikeExtensions.length; i < length; i++) {
			char[] extension = phpLikeExtensions[i];
			int extensionLength = extension.length;
			int extensionStart = fileNameLength - extensionLength;
			if (extensionStart - 1 < 0)
				continue;
			if (fileName[extensionStart - 1] != '.')
				continue;
			for (int j = 0; j < extensionLength; j++) {
				if (fileName[extensionStart + j] != extension[j])
					continue extensions;
			}
			return true;
		}
		return false;
	}

}
